# -*- coding: utf-8 -*-
# Project : flacon
# File : encoder.py
# (c) Alexander Sokolov sokoloff.a@gmail.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.


#import errno
import exceptions
import re
import sys
import time


from PyQt4 import QtCore
from PyQt4.QtCore import *
from PyQt4 import QtGui

from flaconglobal import *
from project import *
from converter import *
from errors import *


class Encoder(ConverterThread):
   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def check(cls, project):
        Encoder.checkPrograms(cls.programs())


   #****************************************************************
   #
   #****************************************************************
    @staticmethod
    def checkPrograms(progs):
        for p in progs:
            if not settings.checkProgram('Programs/' + p):
                raise errors.ProgramNotFoundError(p)


   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def trackGainClass(cls):
        return None


   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def albumGainClass(cls):
        return cls.trackGainClass()


   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def configPageClass(cls):
        return None


   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def programs(cls):
        raise AbstractError(cls)


   #****************************************************************
   #
   #****************************************************************
    def __init__(self, disc, track):
        ConverterThread.__init__(self, disc)
        self._wavFile = QString()
        self._outFile = QString()
        self._proc = None
        self._track = track
        self._trackIndex = track.index()
        self._outFile = track.resultFilePath()


   #****************************************************************
   #
   #****************************************************************
    def track(self):
        return self._track


   #****************************************************************
   #
   #****************************************************************
    def inputDataReady(self, discIndex, trackIndex, fileName):
        if self._trackIndex != trackIndex:
            return

        self._wavFile = QString(fileName)

        if self.isReadyStart():
            self.progressChanged(Statuses.Queued, -1)
            self.emit(SIGNAL("readyStart()"))


   #****************************************************************
   #
   #****************************************************************
    def wavFile(self):
        return self._wavFile


   #****************************************************************
   #
   #****************************************************************
    def isReadyStart(self):
        return not self._wavFile.isEmpty() and self._track


   #****************************************************************
   #
   #****************************************************************
    def _run(self):
        self.progressChanged(Statuses.Encoding, 0)

        if self._cancel: raise Abort()
        self.prepare()

        if self._cancel: raise Abort()
        self.encode()

        if self._cancel: raise Abort()
        self.deleteWavFile()

        self.trackReady(self._outFile)


   #****************************************************************
   #
   #****************************************************************
    def prepare(self):
        outFile = QFile(self._track.resultFilePath())
        outDir = QFileInfo(outFile).dir()

        if outFile.exists() and not outFile.remove():
            raise ConvertError(self.tr("Can't delete file:\n%1\n%2").arg(outFile.fileName()).arg(outFile.errorString()))


   #****************************************************************
   #
   #****************************************************************
    def encode(self):
        args = self.getEncoderArgs()
        #print unicode(args.join(" "))

        self._proc = tryPopen(args, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)

        progress = 0

        total = os.path.getsize(unicode(self._wavFile))

        ready = 0
        buff = ''
        buffSize = int(total / 200)

        f = open(unicode(self._wavFile), 'rb')
        while True:

            if self._cancel:
                self._proc.terminate()
                self._proc.wait()
                raise Abort()

            buff = f.read(buffSize)
            if not buff: break

            ready+=buffSize
            p = int(ready * 100 / total)
            if (progress != p):
                progress = p
                self.progressChanged(Statuses.Encoding, progress)

            try:
                self._proc.stdin.write(buff)

            except:
                raise ConvertError(pipeToStr(self._proc.stderr))

        self._proc.stdin.close()
        res = self._proc.wait()
        if res:
            raise ConvertError(pipeToStr(self._proc.stderr))


   #****************************************************************
   #
   #****************************************************************
    def getEncoderArgs(self):
        raise AbstractError(cls)


   #****************************************************************
   #
   #****************************************************************
    def deleteWavFile(self):
        f = QFile(self._wavFile)
        if f.exists() and not f.remove():
            raise ConvertError(self.tr("Can't delete file:\n%1\n%2").arg(f.fileName()).arg(f.errorString()))



class Gain(ConverterThread):
    TypeDisable     = 'Disable'
    TypeTrack       = 'Track'
    TypeAlbum       = 'Album'

   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def check(cls, project):
        Encoder.checkPrograms(cls.programs())


   #****************************************************************
   #
   #****************************************************************
    @staticmethod
    def checkPrograms(progs):
        for p in progs:
            if not settings.checkProgram('Programs/' + p):
                raise errors.ProgramNotFoundError(p)

   #****************************************************************
   #
   #****************************************************************
    def __init__(self, disc, trackIndexses):
        ConverterThread.__init__(self, disc)
        self._files = QStringList()
        self._trackIndexses = trackIndexses[:]


   #****************************************************************
   #
   #****************************************************************
    def inputDataReady(self, diskIndex, trackIndex, fileName):
        self._trackIndex = trackIndex
        self._files << QString(fileName)
        self.progressChanged(Statuses.WaitGain, -1)

        if self.isReadyStart():
            self.emit(SIGNAL("readyStart()"))


   #****************************************************************
   #
   #****************************************************************
    def isReadyStart(self):
        return self._files.count() == len(self._trackIndexses)


   #****************************************************************
   #
   #****************************************************************
    def _run(self):
        self._cancel = False

        for i in self._trackIndexses:
            self.progressChanged(Statuses.CalcGain, -1,  i)

        args = self.getArgs()
        #print args.join(" ")

        proc = tryPopen(args, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)

        self.parseOut(proc)
        res = proc.wait()
        if res:
            raise ConvertError(pipeToQString(proc.stderr))

        for i in xrange(0, len(self._trackIndexses)):
            self.trackReady(self._files[i], self._trackIndexses[i])


   #****************************************************************
   #
   #****************************************************************
    def getArgs(self):
         raise TypeError('Abstract method "ReplayGain.getArgs" called')


   #****************************************************************
   #
   #****************************************************************
    def parseOut(self, proc):
        pass


   #****************************************************************
   #
   #****************************************************************
    @classmethod
    def programs(cls):
        raise AbstractError(cls)


#*******************************************************************
#
#*******************************************************************
def registerEncoder(encoderClass):
    outputAudioFormats.add(encoderClass)
